String clazzTpl({nameCode, svgCase}) => """
import 'dart:ui';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:path_parsing/path_parsing.dart';

enum IconFontName {
  ///自动生成，误自己添加
$nameCode
}

enum AnimType {
  ///自动生成，误自己添加
  ///没有动画
  none,
  ///正向
  forward,
  ///反向
  reverse,
  ///正向重复
  repeat,
  ///反向重复
  reverseRepeat,
}

typedef OnStart = void Function();
typedef OnEnd = void Function();

/// 请叫我code哥
/// Generates Dart code
class IconFont extends StatefulWidget {
  ///自动生成，误自己添加
  IconFont(
    this.name, {
    Key? key,
    this.size = 40,
    this.color,
    this.colors,
    this.curve = Curves.ease,
    this.paintingStyle = PaintingStyle.fill,
    this.strokeWidth = 1,
    this.duration = const Duration(seconds: 3),
    this.animType = AnimType.none,
    this.loop = 1,
    this.onStart,
    this.onEnd,
  }) : super(key: key);
  final IconFontName name;
  final Color? color;
  final List<Color>? colors;
  final double size;
  final Curve curve;
  final PaintingStyle paintingStyle;
  final double strokeWidth;
  final Duration duration;
  final AnimType animType;
  final int loop;
  final OnStart? onStart;
  final OnEnd? onEnd;

  @override
  _IconFontState createState() => _IconFontState();
}

class _IconFontState extends State<IconFont>
    with SingleTickerProviderStateMixin {
  AnimationController? _animationController;
  int _loop = 1;

  String getColor(int index, String defColor) {
    if (widget.color != null) {
      return widget.color!.toHex();
    }
    if (widget.colors != null &&
        widget.colors!.isNotEmpty &&
        widget.colors!.length > index) {
      return widget.colors!.elementAt(index).toHex();
    }
    return defColor;
  }
  
  ParsPathModel? get pathDataMap {
    switch (widget.name) {
$svgCase
      default:
        return null;
    }
  }

  @override
  void initState() {
    _loop = widget.loop;
    _animationController =
        AnimationController(duration: widget.duration, vsync: this);
    switch (widget.animType) {
      case AnimType.none:
        _animationController?.forward(from: 1);
        break;
      case AnimType.forward:
        if (widget.onStart != null) widget.onStart!();
        _animationController?.forward(from: 0);
        break;
      case AnimType.reverse:
        if (widget.onStart != null) widget.onStart!();
        _animationController?.reverse(from: 1.0);
        break;
      case AnimType.repeat:
        if (widget.onStart != null) widget.onStart!();
        _animationController?.repeat();
        break;
      case AnimType.reverseRepeat:
        if (widget.onStart != null) widget.onStart!();
        _animationController?.repeat(reverse: true);
        break;
    }
    _animationController?.addStatusListener((status) {
      if (status == AnimationStatus.completed &&
          widget.animType == AnimType.forward &&
          _loop > 0) {
        if (_loop > 1) {
          _loop--;
          _animationController?.forward(from: 0);
        } else {
          if (widget.onEnd != null) widget.onEnd!();
        }
      }
      if (status == AnimationStatus.dismissed &&
          widget.animType == AnimType.reverse &&
          _loop > 0) {
        if (_loop > 1) {
          _loop--;
          _animationController?.reverse(from: 1);
        } else {
          if (widget.onEnd != null) widget.onEnd!();
        }
      }
    });
    super.initState();
  }

  @override
  void dispose() {
    _animationController?.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _animationController!,
      builder: (context, child) {
        return Center(
          child: CustomPaint(
            size: Size(widget.size, widget.size),
            painter: IconFontPainter(
              animationController: _animationController!,
              pathDataMap: pathDataMap!,
              curve: widget.curve,
              paintingStyle: widget.paintingStyle,
              strokeWidth: widget.strokeWidth,
            ),
          ),
        );
      },
    );
  }
}

class IconFontPainter extends CustomPainter {
  final AnimationController animationController;
  final Curve curve;
  final ParsPathModel pathDataMap;
  final PaintingStyle paintingStyle;
  final double strokeWidth;

  IconFontPainter({
    required this.animationController,
    this.curve = Curves.ease,
    required this.pathDataMap,
    this.paintingStyle = PaintingStyle.fill,
    this.strokeWidth = 1,
  });

  @override
  void paint(Canvas canvas, Size size) {
    assert(size.width == size.height);
    canvas.save();

    ViewBoxModel viewBox = pathDataMap.viewBox;
    List<PathInfoModel> pathData = pathDataMap.pathList;
    final canvasMin = size.width;
    final viewBoxMax = max(viewBox.width, viewBox.height);
    final scale = canvasMin / viewBox.width;
    // 渲染宽高
    final translateY =
        (canvasMin - viewBox.height * canvasMin / viewBoxMax) / 2;
    final translateX =
        (canvasMin - viewBox.width * canvasMin / viewBoxMax) / 2;
    // 先平移再缩放，不然要除缩放比
    canvas.translate(translateX, translateY);
    canvas.scale(scale, scale);
    _drawPath(pathData, canvas, scale);

    canvas.restore();
  }

  void _drawPath(List<PathInfoModel> pathsData, Canvas canvas, double scale) {
    List<ExtractPathModel> extractPathList = [];
    List<CanvasPathModel> canvasPathMap = [];
    List.generate(pathsData.length, (index) {
      String pathData = pathsData[index].path;
      Color fillColor = HexColor.fromHex(pathsData[index].fillColor)
          .withOpacity(pathsData[index].opacity);
      Path path = Path();
      writeSvgPathDataToPath(pathData, PathPrinter(path: path));
      List<PathMetric> listData = path.computeMetrics().toList();
      extractPathList.addAll(listData.map((e) {
        return ExtractPathModel(
            fillColor: fillColor,
            pathMetric: e,
            pathIndex: index,
            pathTotalNumber: listData.length);
      }));
      canvasPathMap.add(CanvasPathModel(path: path, fillColor: fillColor));
    });
    final extractPathLen = extractPathList.length;
    // 记录每组完成进度
    Map<int, int> progress = {};
    List.generate(extractPathLen, (index) {
      ExtractPathModel item = extractPathList[index];
      PathMetric pathMetric = item.pathMetric;
      Color fillColor = item.fillColor;
      int pathIndex = item.pathIndex;
      int pathTotalNumber = item.pathTotalNumber;
      if (progress.containsKey(pathIndex)) {
        var tmp = progress[pathIndex]!;
        progress[pathIndex] = tmp + 1;
      } else {
        progress[pathIndex] = 1;
      }
      var begin = index / extractPathLen;
      var end = (index + 1) / extractPathLen;
      Animation<double> animation = Tween<double>(begin: 0, end: 1.0).animate(
          CurvedAnimation(
              parent: animationController,
              curve: Interval(begin, end, curve: curve)));
      Paint paint = Paint();
      // 还原strokeWidth
      paint.strokeWidth = strokeWidth / scale;
      paint.strokeCap = StrokeCap.round;
      paint.style = PaintingStyle.stroke;
      if (animation.value > 0) {
        Path extractPath =
            pathMetric.extractPath(0, pathMetric.length * animation.value);
        paint.color = fillColor;
        canvas.drawPath(extractPath, paint);
        // 不依赖animationController value
        Animation<double> animationFill = Tween<double>(begin: 0, end: 1.0)
            .animate(CurvedAnimation(
                parent: animationController,
                curve: Interval(0, 1.0, curve: curve)));
        if (animation.value == 1.0 &&
            paintingStyle == PaintingStyle.fill &&
            progress[pathIndex] == pathTotalNumber) {
          Paint paintPath = Paint();
          paintPath.strokeWidth = 0;
          paintPath.strokeCap = StrokeCap.round;
          paintPath.style = PaintingStyle.fill;
          paintPath.color = canvasPathMap[pathIndex].fillColor;
          canvas.drawPath(canvasPathMap[pathIndex].path, paintPath);
        } else if (animationFill.value == 1.0 &&
            paintingStyle == PaintingStyle.stroke) {
          canvasPathMap.forEach((element) {
            Paint paintFill = Paint();
            paintFill.strokeCap = StrokeCap.round;
            paintFill.style = PaintingStyle.fill;
            paintFill.color = element.fillColor;
            canvas.drawPath(element.path, paintFill);
          });
        }
      }
    });
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return oldDelegate != this;
  }
}

class ExtractPathModel {
  final Color fillColor;
  final PathMetric pathMetric;
  final int pathIndex;
  final int pathTotalNumber;

  const ExtractPathModel(
      {required this.fillColor,
      required this.pathMetric,
      required this.pathIndex,
      required this.pathTotalNumber});
}

class CanvasPathModel {
  final Path path;
  final Color fillColor;

  const CanvasPathModel({required this.path, required this.fillColor});
}

class PathPrinter extends PathProxy {
  final Path path;

  PathPrinter({required this.path});

  @override
  void close() {
    path.close();
  }

  @override
  void cubicTo(
      double x1, double y1, double x2, double y2, double x3, double y3) {
    path.cubicTo(x1, y1, x2, y2, x3, y3);
  }

  @override
  void lineTo(double x, double y) {
    path.lineTo(x, y);
  }

  @override
  void moveTo(double x, double y) {
    path.moveTo(x, y);
  }
}

class ParsPathModel {
  final String name;
  final List<PathInfoModel> pathList;
  final ViewBoxModel viewBox;

  const ParsPathModel(
      {required this.name, required this.pathList, required this.viewBox});
}

class ViewBoxModel {
  final double minX;
  final double minY;
  final double width;
  final double height;

  const ViewBoxModel(
      {required this.minX,
      required this.minY,
      required this.width,
      required this.height});
}

class PathInfoModel {
  final String path;
  final String fillColor;
  final double opacity;

  const PathInfoModel(
      {required this.path, required this.fillColor, required this.opacity});
}

extension HexColor on Color {
  /// String is in the format "aabbcc" or "ffaabbcc" with an optional leading "#".
  static Color fromHex(String hexString) {
    final buffer = StringBuffer();
    if (hexString.length == 6 || hexString.length == 7) buffer.write('ff');
    buffer.write(hexString.replaceFirst('#', ''));
    return Color(int.parse(buffer.toString(), radix: 16));
  }

  /// Prefixes a hash sign if [leadingHashSign] is set to `true` (default is `true`).
  String toHex({bool leadingHashSign = true}) => '\${leadingHashSign ? '#' : ''}'
      '\${alpha.toRadixString(16).padLeft(2, '0')}'
      '\${red.toRadixString(16).padLeft(2, '0')}'
      '\${green.toRadixString(16).padLeft(2, '0')}'
      '\${blue.toRadixString(16).padLeft(2, '0')}';
}
""";
